Authors: J. Coelho, S. Ammer

library(ggplot2)
library(Momocs) # package for outline and curve analysis
## This is Momocs 1.1.0
## 
## Attaching package: 'Momocs'
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:base':
## 
##     table
setwd("/Users/del/ACADEMY/WIP/HumerusProject/Photos_final/") # location of my data

rawLPF <- import_tps(tps.path = "LPF/LPF.TPS", curves = T) # read data Left, Posterior, Females
rawLPM <- import_tps(tps.path = "LPM/LPM.TPS", curves = T) # read data Left, Posterior, Males

Rebuild dataset

For some reason, I can’t use Momocs::Out() directly on my imported TPS. Tried many ways, always obtaining different errors. I thought this might be because the p of coordinates is not consistent. So I resample them. In my understanding coo_sample() works well for open curves, while I preferred coo_interpolate() for my closed outlines, since they had very inconsistent p.

# Rebuild datasets
# For females

for (i in seq_along(rawLPF$cur)){
    # open (curves)
    rawLPF$cur[[i]][[1]] <- coo_sample(rawLPF$cur[[i]][[1]], n = 16)
    # closed (outlines)
    rawLPF$cur[[i]][[2]] <- coo_interpolate(rawLPF$cur[[i]][[2]], n = 24)
    # keep the IDs
    names(rawLPF$cur)[[i]] <- names(rawLPF$cur)[[i]]
}

## For males

for (i in seq_along(rawLPM$cur)){
    # open (curves)
    rawLPM$cur[[i]][[1]] <- coo_sample(rawLPM$cur[[i]][[1]], n = 16)
    # closed (outlines)
    rawLPM$cur[[i]][[2]] <- coo_interpolate(rawLPM$cur[[i]][[2]], n = 24)
    # keep the IDs
    names(rawLPM$cur)[[i]] <- names(rawLPM$cur)[[i]]
}

Spliting data

I tried to work with my curves together but for some reason, I also got errors trying to Out() these. So first I will split my open curve (TE: Throclear Extension) from my closed outlines (OF: Olecranon Fossa), but combine my males and females datasets to see the differences among these groups.

TE.f <- list()
for (i in seq_along(rawLPF$cur)){
    TE.f[[i]] <- rawLPF$cur[[i]][[1]]
    names(TE.f)[[i]] <- names(rawLPF$cur)[[i]]
}

TE.m <- list()
for (i in seq_along(rawLPM$cur)){
    TE.m[[i]] <- rawLPM$cur[[i]][[1]]
    names(TE.m)[[i]] <- names(rawLPM$cur)[[i]]
}

Sex <- rep("Female", length(TE.f))
TE.f <- Out(TE.f, fac = as.data.frame(Sex))
Sex <- rep("Male", length(TE.m))
TE.m <- Out(TE.m, fac = as.data.frame(Sex))
TE <- combine(TE.f, TE.m)

###

OF.f <- list()
for (i in seq_along(rawLPF$cur)){
    OF.f[[i]] <- rawLPF$cur[[i]][[2]]
    names(OF.f)[[i]] <- names(rawLPF$cur)[[i]]
}

OF.m <- list()
for (i in seq_along(rawLPM$cur)){
    OF.m[[i]] <- rawLPM$cur[[i]][[2]]
    names(OF.m)[[i]] <- names(rawLPM$cur)[[i]]
}

Sex <- rep("Female", length(OF.f))
OF.f <- Out(OF.f, fac = as.data.frame(Sex))
Sex <- rep("Male", length(OF.m))
OF.m <- Out(OF.m, fac = as.data.frame(Sex))
OF <- combine(OF.f, OF.m)

Procrustes and Rotation

My pics were (anatomically) upside-down. So I use the very useful coo_rotate() by Pi radians (180º). I have done some landmarks-based analysis before were Procrustes was a mandatory step. I am not sure if it is with open curves and closed outlines, however I am also doing a superimposition step here.

TE.aligned <- TE %>% coo_rotate(.,theta = pi) %>% fgProcrustes() %>% Opn()
OF.aligned <- OF %>% coo_rotate(.,theta = pi) %>% fgProcrustes() %>% coo_close()

Throclear Extension

TE.aligned %>% stack(., title = "Trochlear Extension (H. sapiens)", fac = "Sex")

ftimes <- rep('red', length(grep('Female', TE.aligned$fac$Sex)))
mtimes <- rep('blue', length(grep('Male', TE.aligned$fac$Sex)))
panel(TE.aligned, borders = c(ftimes, mtimes))

TE.aligned %>% npoly() %>% PCA() %>% plot(.,"Sex") # I need to read about this
## 'nb.pts' missing and set to: 16
## 'degree' missing and set to: 5

TE.aligned %>% opoly() %>% PCA() %>% plot(.,"Sex") # read about this
## 'nb.pts' missing and set to 16
## 'degree' missing and set to 5

TE.aligned %>% dfourier() %>% PCA() %>% plot(.,"Sex") # strange results; might not be proper method for this dataset.
## 'nb.h' not provided and set to 12
## ===========================================================================

TE.n <- npoly(TE.aligned, nb.pts = 16, degree = 5)
TE.pca <- PCA(TE.n)
TE.pca %>% as_df() %>% ggplot() +
    aes(x = PC1, y = PC2, col = Sex) + coord_equal() + 
    geom_point() + geom_density2d() + theme_light()

# mean shape:
TE.n %>% mshapes() %>% coo_plot()
## no 'fac' provided, returns meanshape

# mean shape per group:
TE.ms <- mshapes(TE.n, 1)
TEfffs <- TE.ms$shp$Female %T>% coo_plot(border = "red")
TEmmms <- TE.ms$shp$Male %T>% coo_draw(border = "blue")
legend("topright", lwd = 1, col = c("red", "blue"), legend = c("Female", "Male"), cex = 0.8)
title(main = "Throclear Extension - Mean shapes")

# TPS plots
coo_lolli(TEfffs, TEmmms); title("Deformations")

coo_arrows(TEfffs, TEmmms); title("Deformations")
## Warning in arrows(coo1[s, 1], coo1[s, 2], coo2[s, 1], coo2[s, 2], length =
## length, : zero-length arrow is of indeterminate angle and so skipped

tps_grid(TEfffs, TEmmms)

tps_arr(TEfffs, TEmmms)

tps_iso(TEfffs, TEmmms)

# LDA / CVA analysis

TE.lda <- LDA(TE.pca, 1)
## 0.99 total variance
## 2 PC retained
TE.lda
##  * Leave-one-out cross-validation ($CV.correct): (57.6% - 87/151): 
## 
##  * Class correctness ($CV.ce):
##    Female      Male 
## 0.7125000 0.4225352 
## 
##  * Cross-validation table ($CV.tab):
##         classified
## actual   Female Male
##   Female     57   23
##   Male       41   30
plot(TE.lda)

## NULL
plot_CV(TE.lda)

plot_CV2(TE.lda)

Olecranon Fossa

OF.aligned %>% stack(.,title = "Olecranon Fossa (H. sapiens)", fac = "Sex")

OF.aligned %>% panel(., fac = "Sex")

OF.aligned %>% efourier(norm=TRUE) %>% PCA() %>% plot(.,"Sex")
## 'nb.h' not provided and set to 6 (99% harmonic power)

OF.f <- efourier(OF.aligned, nb.h = 6)
OF.pca <- PCA(OF.f)
OF.pca %>% as_df() %>% ggplot() +
    aes(x = PC1, y = PC2, col = Sex) + coord_equal() + 
    geom_point() + geom_density2d() + theme_light()

# mean shape:
OF.f %>% mshapes() %>% coo_plot()
## no 'fac' provided, returns meanshape

# mean shape per group:
OF.ms <- mshapes(OF.f, 1)
OFfffs <- OF.ms$shp$Female %T>% coo_plot(border = "red")
OFmmms <- OF.ms$shp$Male %T>% coo_draw(border = "blue")
legend("topright", lwd = 1, col = c("red", "blue"), legend = c("Female", "Male"), cex = 0.8)
title(main = "Olecranon Fossa - Mean shapes")

# TPS plots
coo_lolli(OFfffs, OFmmms); title("Deformations")

coo_arrows(OFfffs, OFmmms); title("Deformations")

tps_grid(OFfffs, OFmmms)

tps_arr(OFfffs, OFmmms)

tps_iso(OFfffs, OFmmms)

# LDA / CVA analysis

OF.lda <- LDA(OF.pca, 1)
## 0.99 total variance
## 7 PC retained
OF.lda
##  * Leave-one-out cross-validation ($CV.correct): (92.1% - 139/151): 
## 
##  * Class correctness ($CV.ce):
##    Female      Male 
## 0.9500000 0.8873239 
## 
##  * Cross-validation table ($CV.tab):
##         classified
## actual   Female Male
##   Female     76    4
##   Male        8   63
plot(OF.lda)

## NULL
plot_CV(OF.lda)

plot_CV2(OF.lda)

LS0tCnRpdGxlOiAiRGlzdGFsIEh1bWVyaSBTaGFwZSBBbmFseXNpcyBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKKkF1dGhvcnM6KiBKLiBDb2VsaG8sIFMuIEFtbWVyCgpgYGB7ciBpbXBvcnRpbmcgZGF0YSwgY2FjaGU9VFJVRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KE1vbW9jcykgIyBwYWNrYWdlIGZvciBvdXRsaW5lIGFuZCBjdXJ2ZSBhbmFseXNpcwpzZXR3ZCgiL1VzZXJzL2RlbC9BQ0FERU1ZL1dJUC9IdW1lcnVzUHJvamVjdC9QaG90b3NfZmluYWwvIikgIyBsb2NhdGlvbiBvZiBteSBkYXRhCgpyYXdMUEYgPC0gaW1wb3J0X3Rwcyh0cHMucGF0aCA9ICJMUEYvTFBGLlRQUyIsIGN1cnZlcyA9IFQpICMgcmVhZCBkYXRhIExlZnQsIFBvc3RlcmlvciwgRmVtYWxlcwpyYXdMUE0gPC0gaW1wb3J0X3Rwcyh0cHMucGF0aCA9ICJMUE0vTFBNLlRQUyIsIGN1cnZlcyA9IFQpICMgcmVhZCBkYXRhIExlZnQsIFBvc3RlcmlvciwgTWFsZXMKCmBgYAoKIyMgUmVidWlsZCBkYXRhc2V0CgpGb3Igc29tZSByZWFzb24sIEkgY2FuJ3QgdXNlIGBNb21vY3M6Ok91dCgpYCBkaXJlY3RseSBvbiBteSBpbXBvcnRlZCBUUFMuIFRyaWVkIG1hbnkgd2F5cywgYWx3YXlzIG9idGFpbmluZyBkaWZmZXJlbnQgZXJyb3JzLiBJIHRob3VnaHQgdGhpcyBtaWdodCBiZSBiZWNhdXNlIHRoZSAqKnAqKiBvZiBjb29yZGluYXRlcyBpcyBub3QgY29uc2lzdGVudC4gU28gSSByZXNhbXBsZSB0aGVtLiBJbiBteSB1bmRlcnN0YW5kaW5nIGBjb29fc2FtcGxlKClgIHdvcmtzIHdlbGwgZm9yIG9wZW4gY3VydmVzLCB3aGlsZSBJIHByZWZlcnJlZCBgY29vX2ludGVycG9sYXRlKClgIGZvciBteSBjbG9zZWQgb3V0bGluZXMsIHNpbmNlIHRoZXkgaGFkIHZlcnkgaW5jb25zaXN0ZW50ICoqcCoqLgoKYGBge3IgcmVidWlsZCwgY2FjaGU9VFJVRX0KIyBSZWJ1aWxkIGRhdGFzZXRzCiMgRm9yIGZlbWFsZXMKCmZvcgkoaSBpbiBzZXFfYWxvbmcocmF3TFBGJGN1cikpewoJIyBvcGVuIChjdXJ2ZXMpCglyYXdMUEYkY3VyW1tpXV1bWzFdXSA8LSBjb29fc2FtcGxlKHJhd0xQRiRjdXJbW2ldXVtbMV1dLCBuID0gMTYpCgkjIGNsb3NlZCAob3V0bGluZXMpCglyYXdMUEYkY3VyW1tpXV1bWzJdXSA8LSBjb29faW50ZXJwb2xhdGUocmF3TFBGJGN1cltbaV1dW1syXV0sIG4gPSAyNCkKCSMga2VlcCB0aGUgSURzCgluYW1lcyhyYXdMUEYkY3VyKVtbaV1dIDwtIG5hbWVzKHJhd0xQRiRjdXIpW1tpXV0KfQoKIyMgRm9yIG1hbGVzCgpmb3IJKGkgaW4gc2VxX2Fsb25nKHJhd0xQTSRjdXIpKXsKCSMgb3BlbiAoY3VydmVzKQoJcmF3TFBNJGN1cltbaV1dW1sxXV0gPC0gY29vX3NhbXBsZShyYXdMUE0kY3VyW1tpXV1bWzFdXSwgbiA9IDE2KQoJIyBjbG9zZWQgKG91dGxpbmVzKQoJcmF3TFBNJGN1cltbaV1dW1syXV0gPC0gY29vX2ludGVycG9sYXRlKHJhd0xQTSRjdXJbW2ldXVtbMl1dLCBuID0gMjQpCgkjIGtlZXAgdGhlIElEcwoJbmFtZXMocmF3TFBNJGN1cilbW2ldXSA8LSBuYW1lcyhyYXdMUE0kY3VyKVtbaV1dCn0KCmBgYAoKIyMgU3BsaXRpbmcgZGF0YQoKSSB0cmllZCB0byB3b3JrIHdpdGggbXkgY3VydmVzIHRvZ2V0aGVyIGJ1dCBmb3Igc29tZSByZWFzb24sIEkgYWxzbyBnb3QgZXJyb3JzIHRyeWluZyB0byBgT3V0KClgIHRoZXNlLiBTbyBmaXJzdCBJIHdpbGwgc3BsaXQgbXkgb3BlbiBjdXJ2ZSAoVEU6IFRocm9jbGVhciBFeHRlbnNpb24pIGZyb20gbXkgY2xvc2VkIG91dGxpbmVzIChPRjogT2xlY3Jhbm9uIEZvc3NhKSwgYnV0IGNvbWJpbmUgbXkgbWFsZXMgYW5kIGZlbWFsZXMgZGF0YXNldHMgdG8gc2VlIHRoZSBkaWZmZXJlbmNlcyBhbW9uZyB0aGVzZSBncm91cHMuCgpgYGB7ciBzcGxpdCwgY2FjaGU9VFJVRX0KClRFLmYgPC0gbGlzdCgpCmZvcgkoaSBpbiBzZXFfYWxvbmcocmF3TFBGJGN1cikpewoJVEUuZltbaV1dIDwtIHJhd0xQRiRjdXJbW2ldXVtbMV1dCgluYW1lcyhURS5mKVtbaV1dIDwtIG5hbWVzKHJhd0xQRiRjdXIpW1tpXV0KfQoKVEUubSA8LSBsaXN0KCkKZm9yCShpIGluIHNlcV9hbG9uZyhyYXdMUE0kY3VyKSl7CglURS5tW1tpXV0gPC0gcmF3TFBNJGN1cltbaV1dW1sxXV0KCW5hbWVzKFRFLm0pW1tpXV0gPC0gbmFtZXMocmF3TFBNJGN1cilbW2ldXQp9CgpTZXggPC0gcmVwKCJGZW1hbGUiLCBsZW5ndGgoVEUuZikpClRFLmYgPC0gT3V0KFRFLmYsIGZhYyA9IGFzLmRhdGEuZnJhbWUoU2V4KSkKU2V4IDwtIHJlcCgiTWFsZSIsIGxlbmd0aChURS5tKSkKVEUubSA8LSBPdXQoVEUubSwgZmFjID0gYXMuZGF0YS5mcmFtZShTZXgpKQpURSA8LSBjb21iaW5lKFRFLmYsIFRFLm0pCgojIyMKCk9GLmYgPC0gbGlzdCgpCmZvcgkoaSBpbiBzZXFfYWxvbmcocmF3TFBGJGN1cikpewoJT0YuZltbaV1dIDwtIHJhd0xQRiRjdXJbW2ldXVtbMl1dCgluYW1lcyhPRi5mKVtbaV1dIDwtIG5hbWVzKHJhd0xQRiRjdXIpW1tpXV0KfQoKT0YubSA8LSBsaXN0KCkKZm9yCShpIGluIHNlcV9hbG9uZyhyYXdMUE0kY3VyKSl7CglPRi5tW1tpXV0gPC0gcmF3TFBNJGN1cltbaV1dW1syXV0KCW5hbWVzKE9GLm0pW1tpXV0gPC0gbmFtZXMocmF3TFBNJGN1cilbW2ldXQp9CgpTZXggPC0gcmVwKCJGZW1hbGUiLCBsZW5ndGgoT0YuZikpCk9GLmYgPC0gT3V0KE9GLmYsIGZhYyA9IGFzLmRhdGEuZnJhbWUoU2V4KSkKU2V4IDwtIHJlcCgiTWFsZSIsIGxlbmd0aChPRi5tKSkKT0YubSA8LSBPdXQoT0YubSwgZmFjID0gYXMuZGF0YS5mcmFtZShTZXgpKQpPRiA8LSBjb21iaW5lKE9GLmYsIE9GLm0pCgoKYGBgCgojIyBQcm9jcnVzdGVzIGFuZCBSb3RhdGlvbgoKTXkgcGljcyB3ZXJlIChhbmF0b21pY2FsbHkpIHVwc2lkZS1kb3duLiBTbyBJIHVzZSB0aGUgdmVyeSB1c2VmdWwgYGNvb19yb3RhdGUoKWAgYnkgUGkgcmFkaWFucyAoMTgwwropLiBJIGhhdmUgZG9uZSBzb21lIGxhbmRtYXJrcy1iYXNlZCBhbmFseXNpcyBiZWZvcmUgd2VyZSBQcm9jcnVzdGVzIHdhcyBhIG1hbmRhdG9yeSBzdGVwLiBJIGFtIG5vdCBzdXJlIGlmIGl0IGlzIHdpdGggb3BlbiBjdXJ2ZXMgYW5kIGNsb3NlZCBvdXRsaW5lcywgaG93ZXZlciBJIGFtIGFsc28gZG9pbmcgYSBzdXBlcmltcG9zaXRpb24gc3RlcCBoZXJlLgoKYGBge3IgUHJvY3J1c3RlcywgY2FjaGU9VFJVRX0KVEUuYWxpZ25lZCA8LSBURSAlPiUgY29vX3JvdGF0ZSguLHRoZXRhID0gcGkpICU+JSBmZ1Byb2NydXN0ZXMoKSAlPiUgT3BuKCkKT0YuYWxpZ25lZCA8LSBPRiAlPiUgY29vX3JvdGF0ZSguLHRoZXRhID0gcGkpICU+JSBmZ1Byb2NydXN0ZXMoKSAlPiUgY29vX2Nsb3NlKCkKYGBgCgojIFRocm9jbGVhciBFeHRlbnNpb24KCmBgYHtyIFRFIHBsb3R0aW5nLCBjYWNoZT1UUlVFfQoKVEUuYWxpZ25lZCAlPiUgc3RhY2soLiwgdGl0bGUgPSAiVHJvY2hsZWFyIEV4dGVuc2lvbiAoSC4gc2FwaWVucykiLCBmYWMgPSAiU2V4IikKZnRpbWVzIDwtIHJlcCgncmVkJywgbGVuZ3RoKGdyZXAoJ0ZlbWFsZScsIFRFLmFsaWduZWQkZmFjJFNleCkpKQptdGltZXMgPC0gcmVwKCdibHVlJywgbGVuZ3RoKGdyZXAoJ01hbGUnLCBURS5hbGlnbmVkJGZhYyRTZXgpKSkKcGFuZWwoVEUuYWxpZ25lZCwgYm9yZGVycyA9IGMoZnRpbWVzLCBtdGltZXMpKQoKVEUuYWxpZ25lZCAlPiUgbnBvbHkoKSAlPiUgUENBKCkgJT4lIHBsb3QoLiwiU2V4IikgIyBJIG5lZWQgdG8gcmVhZCBhYm91dCB0aGlzClRFLmFsaWduZWQgJT4lIG9wb2x5KCkgJT4lIFBDQSgpICU+JSBwbG90KC4sIlNleCIpICMgcmVhZCBhYm91dCB0aGlzClRFLmFsaWduZWQgJT4lIGRmb3VyaWVyKCkgJT4lIFBDQSgpICU+JSBwbG90KC4sIlNleCIpICMgc3RyYW5nZSByZXN1bHRzOyBtaWdodCBub3QgYmUgcHJvcGVyIG1ldGhvZCBmb3IgdGhpcyBkYXRhc2V0LgoKVEUubiA8LSBucG9seShURS5hbGlnbmVkLCBuYi5wdHMgPSAxNiwgZGVncmVlID0gNSkKVEUucGNhIDwtIFBDQShURS5uKQpURS5wY2EgJT4lIGFzX2RmKCkgJT4lIGdncGxvdCgpICsKCWFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2wgPSBTZXgpICsgY29vcmRfZXF1YWwoKSArIAoJZ2VvbV9wb2ludCgpICsgZ2VvbV9kZW5zaXR5MmQoKSArIHRoZW1lX2xpZ2h0KCkKCiMgbWVhbiBzaGFwZToKVEUubiAlPiUgbXNoYXBlcygpICU+JSBjb29fcGxvdCgpCiMgbWVhbiBzaGFwZSBwZXIgZ3JvdXA6ClRFLm1zIDwtIG1zaGFwZXMoVEUubiwgMSkKVEVmZmZzIDwtIFRFLm1zJHNocCRGZW1hbGUgJVQ+JSBjb29fcGxvdChib3JkZXIgPSAicmVkIikKVEVtbW1zIDwtIFRFLm1zJHNocCRNYWxlICVUPiUgY29vX2RyYXcoYm9yZGVyID0gImJsdWUiKQpsZWdlbmQoInRvcHJpZ2h0IiwgbHdkID0gMSwgY29sID0gYygicmVkIiwgImJsdWUiKSwgbGVnZW5kID0gYygiRmVtYWxlIiwgIk1hbGUiKSwgY2V4ID0gMC44KQp0aXRsZShtYWluID0gIlRocm9jbGVhciBFeHRlbnNpb24gLSBNZWFuIHNoYXBlcyIpCgojIFRQUyBwbG90cwpjb29fbG9sbGkoVEVmZmZzLCBURW1tbXMpOyB0aXRsZSgiRGVmb3JtYXRpb25zIikKY29vX2Fycm93cyhURWZmZnMsIFRFbW1tcyk7IHRpdGxlKCJEZWZvcm1hdGlvbnMiKQp0cHNfZ3JpZChURWZmZnMsIFRFbW1tcykKdHBzX2FycihURWZmZnMsIFRFbW1tcykKdHBzX2lzbyhURWZmZnMsIFRFbW1tcykKCiMgTERBIC8gQ1ZBIGFuYWx5c2lzCgpURS5sZGEgPC0gTERBKFRFLnBjYSwgMSkKVEUubGRhCnBsb3QoVEUubGRhKQpwbG90X0NWKFRFLmxkYSkKcGxvdF9DVjIoVEUubGRhKQoKYGBgCgojIE9sZWNyYW5vbiBGb3NzYQoKYGBge3IgT0YgcGxvdHRpbmcsIGNhY2hlPVRSVUV9CgpPRi5hbGlnbmVkICU+JSBzdGFjayguLHRpdGxlID0gIk9sZWNyYW5vbiBGb3NzYSAoSC4gc2FwaWVucykiLCBmYWMgPSAiU2V4IikKT0YuYWxpZ25lZCAlPiUgcGFuZWwoLiwgZmFjID0gIlNleCIpCgpPRi5hbGlnbmVkICU+JSBlZm91cmllcihub3JtPVRSVUUpICU+JSBQQ0EoKSAlPiUgcGxvdCguLCJTZXgiKQoKT0YuZiA8LSBlZm91cmllcihPRi5hbGlnbmVkLCBuYi5oID0gNikKT0YucGNhIDwtIFBDQShPRi5mKQpPRi5wY2EgJT4lIGFzX2RmKCkgJT4lIGdncGxvdCgpICsKCWFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2wgPSBTZXgpICsgY29vcmRfZXF1YWwoKSArIAoJZ2VvbV9wb2ludCgpICsgZ2VvbV9kZW5zaXR5MmQoKSArIHRoZW1lX2xpZ2h0KCkKCiMgbWVhbiBzaGFwZToKT0YuZiAlPiUgbXNoYXBlcygpICU+JSBjb29fcGxvdCgpCiMgbWVhbiBzaGFwZSBwZXIgZ3JvdXA6Ck9GLm1zIDwtIG1zaGFwZXMoT0YuZiwgMSkKT0ZmZmZzIDwtIE9GLm1zJHNocCRGZW1hbGUgJVQ+JSBjb29fcGxvdChib3JkZXIgPSAicmVkIikKT0ZtbW1zIDwtIE9GLm1zJHNocCRNYWxlICVUPiUgY29vX2RyYXcoYm9yZGVyID0gImJsdWUiKQpsZWdlbmQoInRvcHJpZ2h0IiwgbHdkID0gMSwgY29sID0gYygicmVkIiwgImJsdWUiKSwgbGVnZW5kID0gYygiRmVtYWxlIiwgIk1hbGUiKSwgY2V4ID0gMC44KQp0aXRsZShtYWluID0gIk9sZWNyYW5vbiBGb3NzYSAtIE1lYW4gc2hhcGVzIikKCiMgVFBTIHBsb3RzCmNvb19sb2xsaShPRmZmZnMsIE9GbW1tcyk7IHRpdGxlKCJEZWZvcm1hdGlvbnMiKQpjb29fYXJyb3dzKE9GZmZmcywgT0ZtbW1zKTsgdGl0bGUoIkRlZm9ybWF0aW9ucyIpCnRwc19ncmlkKE9GZmZmcywgT0ZtbW1zKQp0cHNfYXJyKE9GZmZmcywgT0ZtbW1zKQp0cHNfaXNvKE9GZmZmcywgT0ZtbW1zKQoKIyBMREEgLyBDVkEgYW5hbHlzaXMKCk9GLmxkYSA8LSBMREEoT0YucGNhLCAxKQpPRi5sZGEKcGxvdChPRi5sZGEpCnBsb3RfQ1YoT0YubGRhKQpwbG90X0NWMihPRi5sZGEpCgpgYGAKCg==